﻿using UnityEngine;
using System.Collections;

namespace CalmNine.Framework 
{
    [RequireComponent(typeof(AudioSource))]
    public class AudioPlayer : MonoBehaviour
    {
        private AudioGroup mAudioGroup;
        private AudioSource mAudioSource;
        private AudioParam param;
        private int mPlayTime = 0;
        private int mSerialId = 0;
        public bool IsPlaying => mAudioSource.isPlaying;

        public float Length => IsUsing ? param.Clip.length: 0;

        public float LeftTime => IsUsing ? (Length - Time) / Pitch: 0;

        /// <summary>
        /// 正在使用中, 暂停或者播放中
        /// </summary>
        public bool IsUsing => param != null;

        public float Time
        {
            get { return mAudioSource.time; }
            set { mAudioSource.time = value; }
        }
        
        public int Priority
        {
            get { return 128 - mAudioSource.priority; }
            set { mAudioSource.priority = 128 - value; }
        }

        public float Volume
        {
            get { return mAudioSource.volume; }
            set { this.mAudioSource.volume = value; }
        }

        public float Pitch
        {
            get { return mAudioSource.pitch; }
            set { mAudioSource.pitch = value; }
        }

        public float Progress
        {
            get
            {
                if (IsUsing)
                {
                    return mAudioSource.time / mAudioSource.clip.length;
                }
                return 0;
            }
        }

        private void Awake()
        {
            mAudioSource = gameObject.GetComponent<AudioSource>();
            mAudioSource.playOnAwake = false;
        }

        public void Init(AudioGroup audioGroup)
        {
            this.mAudioGroup = audioGroup;
        }

        public void Play(int mSerialId, AudioParam param)
        {
            // 正在播放的音频被中断
            if (IsUsing)
            {
                OnInterrupt();
            }
            mPlayTime = 0;
            this.mSerialId = mSerialId;
            this.param = param;
            this.Priority = param.Priority;
            this.Volume = Volume;
            this.Pitch = param.Pitch;
            StopAllCoroutines();
            mAudioSource.clip = param.Clip;
            mAudioSource.Play();
            param.OnBegin?.Invoke(this);
            if (param.FadeInSeconds > 0f)
            {
                mAudioSource.volume = 0f;
                StartCoroutine(FadeToVolume(Volume, param.FadeInSeconds));
            }
        }

        private IEnumerator FadeToVolume(float volume, float duration)
        {
            float time = 0f;
            float originalVolume = mAudioSource.volume;
            while (time < duration)
            {
                time += UnityEngine.Time.deltaTime;
                mAudioSource.volume = Mathf.Lerp(originalVolume, volume, time / duration);
                yield return new WaitForEndOfFrame();
            }
            mAudioSource.volume = volume;
        }

        private IEnumerator FadeToStop(float duration)
        {
            yield return FadeToVolume(0, duration);
            OnInterrupt();
        }


        private IEnumerator FadeToPause(float duration)
        {
            yield return FadeToVolume(0, duration);
            mAudioSource.Pause();
            Volume = param.Volume;
        }

        public void Stop()
        {
            StopAllCoroutines();
            if (IsUsing)
            {
                if (param.FadeOutSeconds > 0)
                {
                    StartCoroutine(FadeToStop(param.FadeOutSeconds));
                }
                else
                {
                    OnInterrupt();
                }
            }
        }
        public void Pause()
        {
            StopAllCoroutines();
            if (IsUsing)
            {
                if (param.FadeOutSeconds > 0)
                {
                    StartCoroutine(FadeToPause(param.FadeOutSeconds));
                }
                else
                {
                    mAudioSource.Pause();
                }
            }
        }
        public void Resume()
        {
            StopAllCoroutines();
            if (IsUsing)
            {
                if (param.FadeInSeconds > 0)
                {
                    mAudioSource.volume = 0f;
                    StartCoroutine(FadeToVolume(Volume, param.FadeInSeconds));
                    mAudioSource.UnPause();
                }
                else
                {
                    Volume = param.Volume;
                    mAudioSource.UnPause();
                }
            }
        }

        //播放完成 InvokeMethod
        void OnEnd()
        {
            this.param.OnEnd?.Invoke(this);
            OnFinally();
        }

        /// <summary>
        /// 1. 被其他音频替换
        /// 2. Stop
        /// 3. 绑定对象销毁
        /// </summary>
        void OnInterrupt()
        {
            // 取消OnEnd延迟执行
            if (param != null) 
            {
                param.OnInterrupt?.Invoke(this, Progress);
            }
            OnFinally();
        }

        void OnFinally() 
        {
            //停止淡入淡出异步
            StopAllCoroutines();
            mAudioSource.Stop();
            if (this.param != null)
            { 
                this.param.OnFinally?.Invoke(this);
                param = null;
            }
            mAudioSource.clip = null;
            this.mSerialId = -1;
        }

        private void Update()
        {
            if (IsUsing)
            {
                if (Time >= Length)
                {
                    mPlayTime += 1;
                    // 循环播放 或者 没达到指定播放次数
                    if (param.LoopTime == -1 || mPlayTime < param.LoopTime)
                    {
                        //Debug.Log("Music Loop");
                        Time = 0;
                        mAudioSource.Play();
                    }
                    // 播放完成
                    else
                    {
                        //Debug.Log("播放完成");
                        OnEnd();
                        return;
                    }
                }
                // 如果绑定对象，对象隐藏时中断声音
                if (param.BindingEntity != null)
                {
                    if (param.BindingEntity.activeInHierarchy)
                    {
                        transform.position = param.BindingEntity.transform.position;
                    }
                    else
                    {
                        OnInterrupt();
                        return;
                    }
                }
            }
        }
    }
}
